/**
 *       Copyright (c) 2012 Lukas Zaruba
 *
 *   This file is part of Robot Playground.
 *
 *   Robot Playground is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Robot Playground is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with Robot Playground. If not, see <http://www.gnu.org/licenses/>.
 */
package net.zarubsys.commons.scripting;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import net.zarubsys.commons.scripting.variables.IVariablePool;
import net.zarubsys.commons.scripting.variables.MapVariablePool;

/**
 * Based on scripting language from Media Solutions Slovakia, a.s.
 * @author Lukas Zaruba, lukas.zaruba@gmail.com
 */
public class ScriptingEnvironment {
	
	private static ThreadLocal<List<Exception>> tlExceptions = 
        new ThreadLocal<List<Exception>>() {
		
        	@Override 
        	protected List<Exception> initialValue() {
        		return new ArrayList<Exception>();
        	}
        	
	};

	private final Logger scriptLog;
	private Map<String, IVariablePool> pools = new HashMap<String, IVariablePool>();
	private ScriptLocator locator;
	private IMissingFunctionHandler missingFunctionHandler = new ErrorMissingFunctionHandler();
	private boolean stopped = false;
	
	public ScriptingEnvironment(IVariablePool... variablesPools) {
		this(Arrays.asList(variablesPools), null);
	}
	
	public ScriptingEnvironment(Logger scriptLog, IVariablePool... variablesPools) {
		this(Arrays.asList(variablesPools), scriptLog);
	}
	
	public ScriptingEnvironment(List<IVariablePool> variablePools, Logger scriptLog) {
		for (IVariablePool variablePool : variablePools) {
			pools.put(variablePool.getPoolName(), variablePool);
		}
		
		if (scriptLog == null) {
			this.scriptLog = Logger.getLogger("scriptLog");
		} else {
			this.scriptLog = scriptLog;
		}
	}
	
	public void setMissingFunctionHandler(IMissingFunctionHandler handler) {
		this.missingFunctionHandler = handler;
	}
	
	public IVariablePool getPool(String name) {
		if (!existsPool(name)) {
			pools.put(name, new MapVariablePool(name));
		}
		return pools.get(name);
	}
	
	public void stop() {
		stopped = true;
	}
	
	public boolean isStopped() {
		return stopped;
	}
	
	public Logger getScriptLog() {
		return scriptLog;
	}
	
	public boolean existsPool(String name) {
		return pools.containsKey(name);
	}
	
	public Map<String, IVariablePool> getAllPools() {
		return Collections.unmodifiableMap(pools);
	}
	
	public void addException(Exception e) {
		tlExceptions.get().add(e);
	}
	
	public List<Exception> getExceptions() {
		return new ArrayList<Exception>(tlExceptions.get());	//defense copying to allow garbage collection at the end of this thread life 
	}
	
	public void setCurrentScriptExecutionLocator(ScriptLocator locator) {
		this.locator = locator;
	}
	
	public ScriptLocator getCurrentScriptLocator() {
		return locator;
	}
	
	public IScriptFunction handleMissingFunction(String functionName) {
		return missingFunctionHandler.handle(functionName);
	}
	
	private static class ErrorMissingFunctionHandler implements IMissingFunctionHandler {

		@Override
		public IScriptFunction handle(String functionName) {
			throw new IllegalArgumentException("There is no function for the name '" + functionName + "' registered.");
		}
		
	}
	
}